package com.wjh.www.proxy;

import com.mysql.cj.util.StringUtils;
import com.wjh.www.annotation.Transaction;
import com.wjh.www.annotation.mybatis.*;
import com.wjh.www.dao.SqlRunner;
import com.wjh.www.dao.SqlRunnerImpl;
import com.wjh.www.exception.ServiceException;
import com.wjh.www.proxy.constans.DataType;
import com.wjh.www.util.LoggerUtil;
import com.wjh.www.util.ReflectUtil;
import com.wjh.www.util.TransactionUtil;

import java.lang.reflect.*;
import java.sql.Connection;
import java.util.Arrays;
import java.util.Map;
import java.util.logging.Logger;

/**
 * @author wjh
 * @date 2021/9/10 22:45
 * @Package com.wjh.anyview.proxy
 */
public class MyProxy implements InvocationHandler {
    /**
     * 代理类
     */
    private static final MyProxy MY_PROXY = new MyProxy();
    /**
     * sql执行类
     */
    private static final SqlRunner SQL_RUNNER = new SqlRunnerImpl();
    /**
     * 日志类
     */
    private static final Logger logger = LoggerUtil.getLogger();
    /**
     * 目标对象
     */
    private Object target = null;

    public static MyProxy getInstance() {
        return MY_PROXY;
    }

    public <T> T getProxyInstance(Class<?> proxyInterface) {
        Class<?>[] interfaces = null;
        Class<?> clazz = null;
        //如果是接口
        if (proxyInterface.isInterface()) {
            clazz = proxyInterface;
            interfaces = new Class[]{proxyInterface};
        } else {
            //如果不是接口则创建一个实例对象
            try {
                target = proxyInterface.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
                logger.severe("MyProxy类利用反射创建实例对象失败！");
            }
            clazz = target.getClass();
            interfaces = target.getClass().getInterfaces();
        }

        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnObj;
        //如果方法上有@Insert注解，则执行insert相关方法
        if (method.isAnnotationPresent(Insert.class)) {
            Insert insertAnnotation = method.getAnnotation(Insert.class);
            if ("".equalsIgnoreCase(insertAnnotation.sql())) {
                Object obj = args[0];
                return SQL_RUNNER.insert(obj);
            }
            return SQL_RUNNER.executeUpdate(null, args, insertAnnotation.sql());
        }

        //如果方法上有@Delete注解，则执行delete相关方法
        if (method.isAnnotationPresent(Delete.class)) {
            Delete deleteAnnotation = method.getAnnotation(Delete.class);
            if ("".equalsIgnoreCase(deleteAnnotation.sql())) {
                Object obj = args[0];
                return SQL_RUNNER.delete(obj);
            }
            return SQL_RUNNER.executeUpdate(null, args, deleteAnnotation.sql());
        }

        //如果方法上有@Update注解，则执行update相关方法
        if (method.isAnnotationPresent(Update.class)) {
            Update updateAnnotation = method.getAnnotation(Update.class);
            if ("".equalsIgnoreCase(updateAnnotation.sql())) {
                Object obj = args[0];
                if (args.length == 1) {
                    return SQL_RUNNER.updateById(obj);
                }

                return SQL_RUNNER.updateByMap(obj, (Map<String, Object>) args[1]);
            }
            return SQL_RUNNER.executeUpdate(null, args, updateAnnotation.sql());
        }

        //如果方法上有@Select注解，则执行select相关方法
        if (method.isAnnotationPresent(Select.class)) {
            Select selectAnnotation = method.getAnnotation(Select.class);
            ReturnType returnTypeAnnotation = method.getAnnotation(ReturnType.class);
            DataType type = returnTypeAnnotation.returns();
            String sql;

            //如果注解的sql为空，则进行sql的拼接
            if ("".equals(selectAnnotation.sql())) {
                //这里的args[0]是po类对象，用于设置通配符“？”是数据库字段的sql语句(其实就是参数是一个封装的对象，不是离散的量)
                sql = SQL_RUNNER.getSelectSql(args[0], selectAnnotation.fields());

                switch (type) {
                    case OBJECT:
                        return SQL_RUNNER.queryObject(null, args[0], args[0].getClass(), sql);
                    case LIST:
                        return SQL_RUNNER.queryList(null, args[0], args[0].getClass(), sql);
                    case FIELD:
                        return SQL_RUNNER.queryField(sql, null, args[0]);
                    default:
                        throw new Throwable("ReturnType注解出现异常，请仔细检查");
                }
            }

            //sql语句自己写的，直接获取sql
            if (!StringUtils.isNullOrEmpty(selectAnnotation.sql())) {
                sql = selectAnnotation.sql();
                switch (type) {
                    case OBJECT:
                        return SQL_RUNNER.queryObject(args, null, method.getReturnType(), sql);
                    case LIST:
                        //list集合需要获取的是返回值的泛型的class，而不是返回值的class
                        return SQL_RUNNER.queryList(args, null, ReflectUtil.getMethodReturnGenericType(method), sql);
                    case FIELD:
                        return SQL_RUNNER.queryField(sql, args, null);
                    default:
                        throw new Throwable("ReturnType注解出现异常，请仔细检查");
                }
            }

        }


        // 如果方法上面有@Transaction注解，就进行事务，否则正常执行即可
        if (method.isAnnotationPresent(Transaction.class)) {
            //设置一个连接在当前线程共享
            Connection conn = TransactionUtil.getConnection();
            try {
                //如果没有出现异常就提交事务
                TransactionUtil.beginTransaction(conn);
                returnObj = method.invoke(target, args);
                TransactionUtil.commitTransaction(conn);
            } catch (Exception e) {
                // 代理类执行method时发生异常，回滚事务
                TransactionUtil.rollback(conn);
                logger.severe("事务代理发生异常，事务已回滚，参数：" + Arrays.toString(args));
                //如果发生的异常的是反射异常，则转型为反射异常并抛出
                if (e instanceof InvocationTargetException) {
                    throw ((InvocationTargetException) e).getTargetException();
                } else {
                    throw new ServiceException("代理service类进行事务时发生非反射异常~");
                }
            } finally {
                //设置connection为原始状态，解绑线程上的连接
                TransactionUtil.recoverTransaction(conn);
                TransactionUtil.closeThreadConn();
            }
        } else {
            //如果方法上面没有注解，没有必要事务处理，正常执行即可
            returnObj = method.invoke(target, args);
        }

        return returnObj;
    }
}
